# -*- coding: utf-8 -*-
"""
    libs.sti.models
    ~~~~~~~~~~~~~~

    A brief description goes here.

    :copyright: (c)  27/06/2012  by arruda.
    :license: LICENSE_NAME, see LICENSE_FILE for more details.
"""

import itertools
from django.db import models
from django.db.models import query

class PolymorphicQuerySet(query.QuerySet):
    
    def get(self, *args, **kwargs):
        if kwargs.has_key('model_class'):
            cls = kwargs.pop('model_class')
            kwargs['mod'] = cls.__module__
            kwargs['cls'] = cls.__name__
        return super(PolymorphicQuerySet, self).get(*args, **kwargs)
    
    def filter(self, *args, **kwargs):
        if kwargs.has_key('model_class'):
            cls = kwargs.pop('model_class')
            kwargs['mod'] = cls.__module__
            kwargs['cls'] = cls.__name__
        return super(PolymorphicQuerySet, self).filter(*args, **kwargs)


class PolymorphicManager(models.Manager):
    
    def get_query_set(self):
        return PolymorphicQuerySet(self.model, using=self._db)
    
    def get(self, *args, **kwargs):
        return self.get_query_set().get(*args, **kwargs)
    
    def filter(self, *args, **kwargs):
        return self.get_query_set().filter(*args, **kwargs)


class PolymorphicModel(models.Model):
    
    classes = dict()
    mod = models.CharField(max_length=50)
    cls = models.CharField(max_length=30)
    
    class Meta:
        abstract = True
    
    objects = PolymorphicManager()
    @staticmethod
    def __new__(cls, *args, **kwargs):
        if len(args) > len(cls._meta.fields):
            raise IndexError("Number of args exceeds number of fields")
        c = None
        m = None
        fields_iter = iter(cls._meta.fields)
        for val, field in itertools.izip(args, fields_iter):
            if field.name == 'cls':
                c = val
            elif field.name == 'mod':
                m = val
        if c is None:
            c = kwargs.get('cls', None)
        if m is None:
            m = kwargs.get('mod', None)
        if c is not None:
            assert m is not None
            m = str(m)
            c = str(c)
            if not models.Model.classes.has_key(c):
                cls = getattr(__import__(m, globals(), locals(), [c]), c)
                PolymorphicModel.classes[(m, c)] = cls
            else:
                cls = models.Model.classes[(m, c)]
        return super(PolymorphicModel, cls).__new__(cls, *args, **kwargs)
    
    def save(self, *args, **kwargs):
        if not self.cls:
            self.mod = self.__class__.__module__
            self.cls = self.__class__.__name__
            super(PolymorphicModel, self).save(*args, **kwargs)
